using Sidvall.Business;
using Sidvall.Data;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Sidvall.UI.Entities
{
    public abstract class BusinessBaseCollection<TEntity> : System.Collections.ObjectModel.ObservableCollection<TEntity>,
        IDataRowProvider, IDataRowProvider<TEntity>, Sidvall.Data.ITotalRowCount, Sidvall.Data.IListContextProvider, IBusinessCollectionSettingsProvider, INotifyChildChanged,
        IValidParentProvider
        where TEntity : BusinessBase
    {
        #region Public Members

        public event System.EventHandler<ChildChangedEventArgs> ChildChanged;
        public event System.EventHandler<CollectionChangedEventArgs> CollectionChanging;

        // Public
        #region DataRowStateIsDirty

        public bool DataRowStateIsDirty
        {
            get
            {
                if (this.DeletedItemKeys.Count > 0)
                    return true;
                int iMax = this.Count;
                for (int i = 0; i < iMax; i++)
                {
                    if (this[i].DataRowStateIsDirty)
                        return true;
                }
                return false;
            }
        }

        #endregion
        #region TotalRowCount

        private long? _TotalRowCount;
        public long TotalRowCount
        {
            get
            {
                if (_TotalRowCount != null)
                    return _TotalRowCount.Value;
                return this.Count;
            }
            set
            {
                _TotalRowCount = value;
            }
        }

        #endregion

        // Protected
        #region BusinessSettings

        private System.Lazy<BusinessCollectionSettings> _BusinessSettings = new System.Lazy<BusinessCollectionSettings>(BusinessCollectionSettings.CreateSettingsAll);
        protected BusinessCollectionSettings BusinessSettings
        {
            get
            {
                return _BusinessSettings.Value;
            }
        }

        #endregion
        #region DeletedItemKeys

        private List<string> _DeletedItemKeys;
        protected List<string> DeletedItemKeys
        {
            get
            {
                if (_DeletedItemKeys == null)
                    _DeletedItemKeys = new List<string>();
                return _DeletedItemKeys;
            }
            private set
            {
                _DeletedItemKeys = value;
            }
        }

        #endregion
        #region IsReadOnly

        protected bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        #endregion

        // Public
        #region AddRange

        public void AddRange(IEnumerable<TEntity> items)
        {
            if (items == null)
                return;
            int count = 0;
            using (var manager = new BusinessCollectionSettingsManager(this, BusinessCollectionSettings.CreateSettingsNone()))
            {
                foreach (var item in items)
                {
                    if (item != null)
                    {
                        Add(item);
                        count += 1;
                    }
                }
            }
            if (count < 20)
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, items));
            else
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        #endregion
        #region DataRowIsValid

        public bool DataRowIsValid()
        {
            return DataRowIsValid(BusinessRuleFilter.CreateFilter());
        }
        public virtual bool DataRowIsValid(BusinessRuleFilter policy)
        {
            foreach (var item in this)
            {
                if (!item.DataRowIsValid(policy))
                    return false;
            }
            return true;
        }

        #endregion
        #region NotifyCollectionChanged

        public void NotifyCollectionChanged()
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        #endregion
        #region RemoveRange

        public int RemoveRange(IEnumerable<TEntity> items)
        {
            if (items == null)
                return 0;
            int count = 0;
            using (var manager = new BusinessCollectionSettingsManager(this, BusinessCollectionSettings.CreateSettingsNone()))
            {
                foreach (var item in items)
                {
                    if (item != null)
                    {
                        if (Remove(item))
                            count += 1;
                    }
                }
            }
            if (count < 20)
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, items));
            else
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            return count;
        }

        #endregion
        #region ReplaceItems

        public void ReplaceItems(IEnumerable<TEntity> items)
        {
            if (items == null)
                return;

            using (var manager = new BusinessCollectionSettingsManager(this, BusinessCollectionSettings.CreateSettingsNone()))
            {
                ClearItems(false);
                foreach (var item in items)
                {
                    if (item != null)
                        Add(item);
                }
            }
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }

        #endregion

        // Protected
        #region ClearItems

        protected override void ClearItems()
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyCollectionChanging))
            {
                var args = OnCollectionChanging(CollectionChangedAction.ClearItems);
                if (args.Cancel)
                    return;
            }
            ClearItems(true);
        }
        private void ClearItems(bool markAsDeleted)
        {
            foreach (var item in this)
            {
                RemoveEventHooks(item);
                if (markAsDeleted)
                    DeleteChild(item);
            }
            base.ClearItems();
        }

        #endregion
        #region InsertItem

        protected override void InsertItem(int index, TEntity item)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyCollectionChanging))
            {
                var args = OnCollectionChanging(CollectionChangedAction.InsertItem, item, index);
                if (args.Cancel)
                    return;
            }
            base.InsertItem(index, item);
            AddChild(item);
        }

        #endregion
        #region MoveItem

        protected override void MoveItem(int oldIndex, int newIndex)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyCollectionChanging))
            {
                var args = OnCollectionChanging(CollectionChangedAction.MoveItem, this[oldIndex], oldIndex, newIndex);
                if (args.Cancel)
                    return;
            }
            base.MoveItem(oldIndex, newIndex);
        }

        #endregion
        #region RemoveItem

        protected override void RemoveItem(int index)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyCollectionChanging))
            {
                var args = OnCollectionChanging(CollectionChangedAction.RemoveItem, this[index], index);
                if (args.Cancel)
                    return;
            }
            RemoveItem(index, true);
        }
        private void RemoveItem(int index, bool markAsDeleted)
        {
            var item = this[index];
            RemoveEventHooks(item);
            if (markAsDeleted)
                DeleteChild(item);
            base.RemoveItem(index);
        }

        #endregion
        #region SetItem

        protected override void SetItem(int index, TEntity item)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyCollectionChanging))
            {
                var args = OnCollectionChanging(CollectionChangedAction.SetItem, item, index);
                if (args.Cancel)
                    return;
            }
            SetItem(index, item, true);
        }
        private void SetItem(int index, TEntity item, bool markAsDeleted)
        {
            var child = this[index];
            if (!ReferenceEquals(child, item))
            {
                RemoveEventHooks(child);
                if (markAsDeleted)
                    DeleteChild(child);
                base.SetItem(index, item);
                AddChild(item);
            }
        }

        #endregion

        #region OnChildChanged

        protected void OnChildChanged(object child, PropertyChangedEventArgs childArgs)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyChildChanged))
            {
                var args = new ChildChangedEventArgs(child, childArgs);
                OnChildChanged(args);
            }
        }
        protected void OnChildChanged(object child, NotifyCollectionChangedEventArgs childArgs)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyChildChanged))
            {
                var args = new ChildChangedEventArgs(child, childArgs);
                OnChildChanged(args);
            }
        }
        protected void OnChildChanged(object child, ChildChangedEventArgs e)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyChildChanged))
                OnChildChanged(e);
        }
        protected virtual void OnChildChanged(ChildChangedEventArgs args)
        {
            if (this.ChildChanged != null)
                this.ChildChanged.Invoke(this, args);
        }

        #endregion
        #region OnCollectionChanging

        protected CollectionChangedEventArgs OnCollectionChanging(CollectionChangedAction action)
        {
            return OnCollectionChanging(new CollectionChangedEventArgs(action));
        }
        protected CollectionChangedEventArgs OnCollectionChanging(CollectionChangedAction action, object item, int index)
        {
            return OnCollectionChanging(new CollectionChangedEventArgs(action, item, index));
        }
        protected CollectionChangedEventArgs OnCollectionChanging(CollectionChangedAction action, object item, int oldIndex, int newIndex)
        {
            return OnCollectionChanging(new CollectionChangedEventArgs(action, item, oldIndex, newIndex));
        }
        protected virtual CollectionChangedEventArgs OnCollectionChanging(CollectionChangedEventArgs args)
        {
            if (this.CollectionChanging != null)
                this.CollectionChanging(this, args);
            return args;
        }

        #endregion
        #region OnCollectionChanged

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyCollectionChanged))
                base.OnCollectionChanged(e);
        }

        #endregion
        #region OnPropertyChanged

        protected void OnPropertyChanged(string propertyName)
        {
            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
        }
        protected override void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.BusinessSettings.EnabledBusinessCollectionEvents.HasFlag(BusinessCollectionEvents.NotifyPropertyChanged))
            {
                base.OnPropertyChanged(e);
            }
        }

        #endregion

        #region IBusinessCollectionSettingsProvider Members

        BusinessCollectionSettings IBusinessCollectionSettingsProvider.BusinessSettings
        {
            get
            {
                return this.BusinessSettings;
            }
        }

        #endregion
        #region IDataRowProvider Members

        System.Collections.IEnumerable IDataRowProvider.DataItems
        {
            get
            {
                return this;
            }
        }
        IEnumerable<TEntity> IDataRowProvider<TEntity>.DataItems
        {
            get
            {
                return this;
            }
        }

        #endregion
        #region IListContextProvider Members

        IEnumerable<string> IListContextProvider.DeletedItemKeys
        {
            get
            {
                return this.DeletedItemKeys;
            }
            set
            {
                this.DeletedItemKeys = new List<string>(value);
            }
        }

        #endregion
        #region IValidParentProvider Members

        bool IValidParentProvider.IsValid(BusinessRuleFilter policy)
        {
            return DataRowIsValid(policy);
        }
        bool IValidParentProvider.IsSelfValid(BusinessRuleFilter policy)
        {
            return DataRowIsValid(policy);
        }

        #endregion

        #endregion
        #region Private Members

        #region AddChild

        private void AddChild(TEntity item)
        {
            var dataRowContextProvider = item as IDataRowContextProvider;
            if ((dataRowContextProvider.DataRowContextStorageKey != null) && (this.DeletedItemKeys.Contains(dataRowContextProvider.DataRowContextStorageKey)))
            {
                if (dataRowContextProvider.DataRowStateIsNew)
                    dataRowContextProvider.SetDataRowContext(null, DataRowState.Modified);
                this.DeletedItemKeys.Remove(dataRowContextProvider.DataRowContextStorageKey);
            }
            AddEventHooks(item);
        }

        #endregion
        #region DeleteChild

        private void DeleteChild(IDataRowContextProvider item)
        {
            if (!this.BusinessSettings.MarkAsDeleted)
                return;
            if (item?.DataRowContextStorageKey == null)
                return;
            if (item.DataRowStateIsNew)
                return;
            this.DeletedItemKeys.Add(item.DataRowContextStorageKey);
        }

        #endregion
        #region AddEventHooks

        private void AddEventHooks(TEntity item)
        {
            if (item == null)
                return;
            var pc = item as INotifyPropertyChanged;
            if (pc != null)
                pc.PropertyChanged += OnChildChanged;
            var ncc = item as INotifyCollectionChanged;
            if (ncc != null)
                ncc.CollectionChanged += OnChildChanged;
            var cc = item as INotifyChildChanged;
            if (cc != null)
                cc.ChildChanged += OnChildChanged;
        }

        #endregion
        #region RemoveEventHooks

        private void RemoveEventHooks(object item)
        {
            if (item == null)
                return;
            var pc = item as INotifyPropertyChanged;
            if (pc != null)
                pc.PropertyChanged -= OnChildChanged;
            var ncc = item as INotifyCollectionChanged;
            if (ncc != null)
                ncc.CollectionChanged -= OnChildChanged;
            var cc = item as INotifyChildChanged;
            if (cc != null)
                cc.ChildChanged -= OnChildChanged;
        }

        #endregion

        #endregion
        #region Constructors

        protected BusinessBaseCollection()
        {
        }

        #endregion
    }
}
